package com.xiaoquan.gulimall.auth.controller;

import com.alibaba.fastjson.TypeReference;
import com.xiaoquan.common.constant.AuthServerConstant;
import com.xiaoquan.common.exception.BizCodeEnums;
import com.xiaoquan.common.utils.R;
import com.xiaoquan.common.vo.MemberResponseVo;
import com.xiaoquan.gulimall.auth.feign.MemberFeignService;
import com.xiaoquan.gulimall.auth.feign.ThirdPartyFeignService;
import com.xiaoquan.gulimall.auth.vo.UserLoginVo;
import com.xiaoquan.gulimall.auth.vo.UserRegisterVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @authro dxq
 * @date 2021年07月07日 2:18
 */
/**
 * 需求：发送一个请求直接跳转到一个页面
 * 目的：避免写很多空方法
 * 解决：使用 SpringMVC的 viewcontroller 将请求和页面一一映射
 *
 * @GetMapping("/login.html")
 *     public String loginPage(){
 *         return "login";
 *     }
 *     @GetMapping("/reg.html")
 *     public String regPage(){
 *         return "reg";
 */

@Controller
public class LoginController {

    @Autowired
    ThirdPartyFeignService thirdPartyFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;  // redis的操作工具类

    @Autowired
    MemberFeignService memberFeignService; // 会员注册

    /**
     * 短信发送接口
     * @param phone
     * @return
     */
    //@PathVariable是获取url上数据的。
    //@RequestParam获取请求参数的（包括post表单提交）
    @ResponseBody  // 因为请求的是json数据 所以要加上这个注解
    @GetMapping("/sms/sendcode")
    public R sendCode(@RequestParam("phone") String phone){

        // TODO 1、接口防止别人恶意刷新
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        if (redisCode != null && !redisCode.trim().equals("")) {
            long l = Long.parseLong(redisCode.split("_")[1]);
            // System.currentTimeMillis() 当前时间
            // 判单当前时间 减去 存储时间是否大于60秒
            if ( System.currentTimeMillis() - l < 60000){ //  数字单位是毫秒 所以还要加3个0
                // 60秒内不能再发
                return R.error(BizCodeEnums.SMS_CODE_EXCEPTION.getCode(),BizCodeEnums.SMS_CODE_EXCEPTION.getMsg());
            }
        }
            String code1 = UUID.randomUUID().toString().substring(0, 5); // 要发送给手机的验证码
            String subString = code1+"_"+System.currentTimeMillis(); // 要存入redis中的验证码 加入当前时间戳 防止60s内 同一号码重新发送验证码
            String code2 = "**code**:"+code1;

            // TODO 2、验证码的再次校验  存入redis中  key-phone value-code  sms:code:18679340097 ->123456
            // redis缓存验证码 long var3, TimeUnit var5
            redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone,subString,10, TimeUnit.MINUTES);

            thirdPartyFeignService.sendCode(phone,code2);
            return R.ok();
    }

    /**
     * RedirectAttributes redirectAttributes 模拟重定向携带数据
     * 可以解决 Request method 'POST' not supported 这个问题就是 我们重定向是POST请求 而被请求的页面是要求使用GET请求的页面 这样就会出问题
     *
     * @param vo
     * @param result
     * @param redirectAttributes
     * @return
     */
    @PostMapping("/register")
    public String register(@Valid UserRegisterVo vo, BindingResult result,
                           RedirectAttributes redirectAttributes){ // Model model
        if (result.hasErrors()){
            // 返回错误信息
            /**
             *  TODO 重定向携带数据，
             *  利用session原理。将数据放在session中。
             *      只要跳到下一个页面取出这个数据以后，session里面的数据就会删除
             *  TODO 分布式下的session问题
             *
             * result.getFieldErrors().stream().collect(Collectors.toMap(fieldError -> {
             *   return fieldError.getField();
             * },fieldError -> {
             *  return fieldError.getDefaultMessage();
             *   }));
             */
            Map<String, String> errors = result.getFieldErrors().stream().
                    collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
            // model.addAttribute("errors",errors);
            redirectAttributes.addFlashAttribute("errors",errors);

            // 校验出错，转发到注册页
            // forward转发 return "forward:/reg.html";  Request method 'POST' not supported
            return "redirect:http://39.103.133.67/page/auth/reg.html";
        }

        // 真正注册 调用远程服务进行注册
        String code = vo.getCode();
        String s = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
        if (s != null && !s.trim().equals("")) {
            if (code.equals( s.split("_")[0])){
                // 删除redis中的验证码  令牌的机制
                redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
                //验证码通过   真正注册 调用远程服务进行注册
                R register = memberFeignService.register(vo);
                if (register.getCode() == 0){ // 等于0 是成功

                    return "redirect:http://39.103.133.67/page/auth/login.html"; // /代表以项目的域名的路径为准
                }else { // 其他状态都是失败
                    Map<String, String> errors = new HashMap<>();
                    errors.put("msg",register.getData("msg",new TypeReference<String>(){}));
                    redirectAttributes.addFlashAttribute("errors",errors);
                    return "redirect:http://39.103.133.67/page/auth/reg.html";
                }
            }else {
                // F2、验证码与redis中缓存的验证码不一致
                // 重定向到注册页面 把错误信息也返回
                Map<String, String> errors = new HashMap<>();
                errors.put("code","验证码错误");
                redirectAttributes.addFlashAttribute("errors",errors);
                return "redirect:http://39.103.133.67/page/auth/reg.html";
            }
        }else {
            // F1、从redis缓存中拿取不到验证码 说明 验证码为空 正在输入的手机号与接收发送短信的手机号码不一致
            Map<String, String> errors = new HashMap<>();
            errors.put("code","验证码错误");
            redirectAttributes.addFlashAttribute("errors",errors);
            return "redirect:http://39.103.133.67/page/auth/reg.html";
        }
        // 注册成功返回首页 redirect重定向
       // return "redirect:/login.html"; // /代表以项目的域名的路径为准
    }

    @GetMapping("/login.html")
    public String loginPage(HttpSession session){
        Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);
        // 判断 已经登录过的用户再次访问登录页 就会返回到首页
        if (attribute ==null){
            // 没有登录过 就要跳转到登录页
            return "login";
        }else {
           // 已经登陆过了 就跳转到商品首页
          return "redirect:http://39.103.133.67/page/product/index.html";
        }
    }



    /**
     * 登录接口 验证用户名和密码
     * @return
     */
    @PostMapping("/login")
    public String login(UserLoginVo vo, RedirectAttributes redirectAttributes, HttpSession session){ // 没加@RequestBody  第一个请求接的是页面的 是传的K-V

        // 远程登录
        R login = memberFeignService.login(vo);
        if (login.getCode() == 0){ // 只要是成功 都是0
            MemberResponseVo data = login.getData("data", new TypeReference<MemberResponseVo>() {
            });
            // 将用户信息放入seesion中
            session.setAttribute(AuthServerConstant.LOGIN_USER,data);
            // 成功 返回product首页
            return "redirect:http://39.103.133.67/page/product/index.html";
        }else {
            // 失败 重定向到登录页
            // 无论是多个消息还是单个消息 都放在Map里面
            Map<String, String> errors = new HashMap<>();
            errors.put("msg",login.getData("msg",new TypeReference<String>(){}));
            redirectAttributes.addFlashAttribute("errors",errors);
            return "redirect:http://39.103.133.67/page/auth/login.html";
        }

    }

}
